/*
 *    Copyright 2008-2009 The PoCo Team
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package com.asemantics.poco.parser;

import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.digester.BeanPropertySetterRule;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.ObjectCreateRule;
import org.apache.commons.digester.SetNextRule;
import org.xml.sax.SAXException;

import com.asemantics.poco.Account;
import com.asemantics.poco.Activity;
import com.asemantics.poco.Address;
import com.asemantics.poco.BodyType;
import com.asemantics.poco.CurrentLocation;
import com.asemantics.poco.Email;
import com.asemantics.poco.Entry;
import com.asemantics.poco.IM;
import com.asemantics.poco.MediaItem;
import com.asemantics.poco.Name;
import com.asemantics.poco.OSUrl;
import com.asemantics.poco.Organization;
import com.asemantics.poco.PhoneNumber;
import com.asemantics.poco.Photo;
import com.asemantics.poco.Response;
import com.asemantics.poco.Url;
import com.asemantics.poco.converters.PoCoConverterUtils;

/**
 * 
 * @author Simone Tripodi
 * @version $Id: AbstractPocoParser.java 244 2009-12-20 00:33:30Z simone.tripodi@gmail.com $
 */
public abstract class AbstractPocoParser {

    protected Digester digester;

    protected void initRules() {
        PoCoConverterUtils.checkAndRegisterConverters();

        this.digester.addRule("response", new ObjectCreateRule(Response.class));
        this.digester.addRule("response/startIndex", new BeanPropertySetterRule());
        this.digester.addRule("response/itemsPerPage", new BeanPropertySetterRule());
        this.digester.addRule("response/totalResults", new BeanPropertySetterRule());

        this.digester.addRule("response/entry", new ObjectCreateRule(Entry.class));
        this.digester.addRule("response/entry/id", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/displayName", new BeanPropertySetterRule());

        // --- 7.3. name Element
        this.digester.addRule("response/entry/name", new ObjectCreateRule(Name.class));
        this.digester.addRule("response/entry/name/formatted", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/name/familyName", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/name/givenName", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/name/middleName", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/name/honorificPrefix", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/name/honorificSuffix", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/name", new SetNextRule("setName"));

        this.digester.addRule("response/entry/nickname", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/published", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/updated", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/birthday", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/anniversary", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/gender", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/note", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/preferredUsername", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/utcOffset", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/connected", new BeanPropertySetterRule());
        this.digester.addRule("response/entry", new SetNextRule("addEntry"));

        // OpenSocial fields
        this.digester.addRule("response/entry/aboutMe", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/bodyType", new ObjectCreateRule(BodyType.class));
        this.digester.addRule("response/entry/bodyType/build", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/bodyType/eyeColor", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/bodyType/hairColor", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/bodyType/height", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/bodyType/weight", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/bodyType", new SetNextRule("setBodyType"));

        this.digester.addRule("response/entry/currentLocation", new ObjectCreateRule(CurrentLocation.class));
        this.digester.addRule("response/entry/currentLocation/country", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/currentLocation/extendedAddress", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/currentLocation/latitude", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/currentLocation/locality", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/currentLocation/longitude", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/currentLocation/poBox", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/currentLocation/postalCode", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/currentLocation/region", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/currentLocation/streetAddress", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/currentLocation/type", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/currentLocation/unstructuredAddress", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/currentLocation/formatted", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/currentLocation/primary", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/currentLocation", new SetNextRule("setCurrentLocation"));

        this.digester.addRule("response/entry/drinker", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/ethnicity", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/fashion", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/happiestWhen", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/humor", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/livingArrangement", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/lookingFor", new BeanPropertySetterRule());

        this.digester.addRule("response/entry/profileSong", new ObjectCreateRule(OSUrl.class));
        this.digester.addRule("response/entry/profileSong/address", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/profileSong/linkText", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/profileSong/type", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/profileSong", new SetNextRule("setProfileSong"));

        this.digester.addRule("response/entry/profileVideo", new ObjectCreateRule(OSUrl.class));
        this.digester.addRule("response/entry/profileVideo/address", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/profileVideo/linkText", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/profileVideo/type", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/profileVideo", new SetNextRule("setProfileVideo"));

        this.digester.addRule("response/entry/relationshipStatus", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/religion", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/romance", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/scaredOf", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/sexualOrientation", new BeanPropertySetterRule());

        this.digester.addRule("response/entry/smoker", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/status", new BeanPropertySetterRule());

        // --- 7.2.2. Plural Fields
        this.digester.addRule("response/entry/emails", new ObjectCreateRule(Email.class));
        this.digester.addRule("response/entry/emails/value", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/emails/type", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/emails/primary", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/emails", new SetNextRule("addEmail"));

        this.digester.addRule("response/entry/urls", new ObjectCreateRule(Url.class));
        this.digester.addRule("response/entry/urls/value", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/urls/type", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/urls/primary", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/urls", new SetNextRule("addUrl"));

        this.digester.addRule("response/entry/phoneNumbers", new ObjectCreateRule(PhoneNumber.class));
        this.digester.addRule("response/entry/phoneNumbers/value", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/phoneNumbers/type", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/phoneNumbers/primary", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/phoneNumbers", new SetNextRule("addPhoneNumber"));

        this.digester.addRule("response/entry/ims", new ObjectCreateRule(IM.class));
        this.digester.addRule("response/entry/ims/value", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/ims/type", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/ims/primary", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/ims", new SetNextRule("addIM"));

        this.digester.addRule("response/entry/photos", new ObjectCreateRule(Photo.class));
        this.digester.addRule("response/entry/photos/value", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/photos/type", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/photos/primary", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/photos", new SetNextRule("addPhoto"));

        this.digester.addRule("response/entry/tags", new BeanPropertySetterRule("addTag"));

        this.digester.addRule("response/entry/relationships", new BeanPropertySetterRule("addRelationship"));

        // --- 7.4. address Element
        this.digester.addRule("response/entry/addresses", new ObjectCreateRule(Address.class));
        this.digester.addRule("response/entry/addresses/country", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/addresses/formatted", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/addresses/locality", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/addresses/postalCode", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/addresses/region", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/addresses/streetAddress", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/addresses/type", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/addresses", new SetNextRule("addAddress"));

        // --- 7.5. organization Element
        this.digester.addRule("response/entry/organizations", new ObjectCreateRule(Organization.class));
        this.digester.addRule("response/entry/organizations/name", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/organizations/department", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/organizations/title", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/organizations/type", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/organizations/startDate", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/organizations/endDate", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/organizations/location", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/organizations/description", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/organizations", new SetNextRule("addOrganization"));

        // --- 7.6. account Element
        this.digester.addRule("response/entry/accounts", new ObjectCreateRule(Account.class));
        this.digester.addRule("response/entry/accounts/domain", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/accounts/username", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/accounts/userid", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/accounts", new SetNextRule("addAccount"));

        // OpenSocial Plural Fields
        this.digester.addRule("response/entry/activities", new ObjectCreateRule(Activity.class));
        this.digester.addRule("response/entry/activities/appId", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/body", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/bodyId", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/externalId", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/id", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/postedTime", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/priority", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/streamFaviconUrl", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/streamSourceUrl", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/streamTitle", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/streamUrl", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/title", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/titleId", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/url", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/userId", new BeanPropertySetterRule());

        this.digester.addRule("response/entry/activities/mediaItems", new ObjectCreateRule(MediaItem.class));
        this.digester.addRule("response/entry/activities/mediaItems/mimeType", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/mediaItems/mediaItemType", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/mediaItems/url", new BeanPropertySetterRule());
        this.digester.addRule("response/entry/activities/mediaItems", new SetNextRule("addMediaItem"));
        this.digester.addRule("response/entry/activities", new SetNextRule("addActivity"));

        this.digester.addRule("response/entry/books", new BeanPropertySetterRule("addBook"));
        this.digester.addRule("response/entry/cars", new BeanPropertySetterRule("addCar"));
        this.digester.addRule("response/entry/children", new BeanPropertySetterRule("addChild"));
        this.digester.addRule("response/entry/foods", new BeanPropertySetterRule("addFood"));
        this.digester.addRule("response/entry/heroes", new BeanPropertySetterRule("addHero"));
        this.digester.addRule("response/entry/interests", new BeanPropertySetterRule("addInterest"));
        this.digester.addRule("response/entry/jobInterests", new BeanPropertySetterRule("addJobInterest"));
        this.digester.addRule("response/entry/languagesSpoken", new BeanPropertySetterRule("addLanguageSpoken"));
        this.digester.addRule("response/entry/movies", new BeanPropertySetterRule("addMovie"));
        this.digester.addRule("response/entry/music", new BeanPropertySetterRule("addMusic"));
        this.digester.addRule("response/entry/pets", new BeanPropertySetterRule("addPet"));
        this.digester.addRule("response/entry/politicalViews", new BeanPropertySetterRule("addPoliticalView"));
        this.digester.addRule("response/entry/quotes", new BeanPropertySetterRule("addQuote"));
        this.digester.addRule("response/entry/sports", new BeanPropertySetterRule("addSport"));
        this.digester.addRule("response/entry/turnOffs", new BeanPropertySetterRule("addTurnOff"));
        this.digester.addRule("response/entry/turnOns", new BeanPropertySetterRule("addTurnOn"));
        this.digester.addRule("response/entry/tvShows", new BeanPropertySetterRule("addTvShow"));
    }

    public final Response parse(InputStream input) throws PoCoParserException {
        try {
            return (Response) this.digester.parse(input);
        } catch (IOException e) {
            throw new PoCoParserException("Fatal trasport exception", e);
        } catch (SAXException e) {
            throw new PoCoParserException("Fatal parse exception", e);
        } finally {
            this.digester.clear();
        }
    }

}
